package com.share.common.utils;

import java.util.Random;

/**
 * 雪花算法返回雪花id
 */
public class SnowFlakeUtil {
    private static SnowFlake snowFlake = new SnowFlake();

    /**
     * 返回雪花id
     *
     * @return
     */
    public static long nextVal() {
        return snowFlake.nextVal();
    }


    /**
     * 内部类
     */
    private static class SnowFlake {

        /**
         * 起始的时间戳
         */
        private final static long START_STAMP = 1480166465631L;

        /**
         * 每一部分占用的位数
         */
        /**
         * 序列号占用的位数
         */
        private final static long SEQUENCE_BIT = 12;
        /**
         * 机器标识占用的位数
         */
        private final static long MACHINE_BIT = 5;
        /**
         * 数据中心占用的位数
         */
        private final static long DATA_CENTER_BIT = 5;

        /**
         * 每一部分的最大值
         */
        private final static long MAX_DATA_CENTER_NUM = -1L ^ (-1L << DATA_CENTER_BIT);
        private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
        private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

        /**
         * 每一部分向左的位移
         */
        private final static long MACHINE_LEFT = SEQUENCE_BIT;
        private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
        private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;

        /**
         * 数据中心
         */
        private long dataCenterId;
        /**
         * 机器标识
         */
        private long machineId;
        /**
         * 序列号
         */
        private long sequence = 0L;
        /**
         * 上一次时间戳
         */
        private long lastStamp = -1L;

        public SnowFlake() {
            Random random = new Random();
            // TODO 这里取值只能是32以内
            int i = random.nextInt(32);
            int j = random.nextInt(32);
            this.dataCenterId = i;
            this.machineId = j;
        }

        public SnowFlake(long dataCenterId, long machineId) {
            if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0) {
                throw new IllegalArgumentException("dataCenterId can't be greater than MAX_DATA_CENTER_NUM or less than 0");
            }
            if (machineId > MAX_MACHINE_NUM || machineId < 0) {
                throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
            }
            this.dataCenterId = dataCenterId;
            this.machineId = machineId;
        }

        /**
         * 产生下一个ID
         *
         * @return
         */
        public synchronized long nextVal() {
            long currStamp = getNewStamp();
            if (currStamp < lastStamp) {
                throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
            }
            if (currStamp == lastStamp) {
                //相同毫秒内，序列号自增
                sequence = (sequence + 1) & MAX_SEQUENCE;
                //同一毫秒的序列数已经达到最大
                if (sequence == 0L) {
                    currStamp = getNextMill();
                }
            } else {
                //不同毫秒内，序列号置为0
                sequence = 0L;
            }
            lastStamp = currStamp;
            /**
             *  时间戳部分
             *  数据中心部分
             *  机器标识部分
             *  序列号部分
             */
            return (currStamp - START_STAMP) << TIMESTAMP_LEFT
                    | dataCenterId << DATA_CENTER_LEFT
                    | machineId << MACHINE_LEFT
                    | sequence;
        }

        private long getNextMill() {
            long mill = getNewStamp();
            while (mill <= lastStamp) {
                mill = getNewStamp();
            }
            return mill;
        }

        private long getNewStamp() {
            return System.currentTimeMillis();
        }
    }
}
